﻿//#define rewriter_debugging

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

using agree.configuration;
using agree.schema;

using alib.Character;
using alib.Collections;
using alib.Debugging;
using alib.Enumerable;
using alib.Lattice;

namespace agree.rewriter
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class ContextSensitiveRewriter<R, TLat, TRuleIn, TRuleOut> : AgreeTrellis<TLat>,
		ρρTfsUnifier
		where R : RewriteRule<TRuleIn, TRuleOut>
		where TLat : class, ITfsSlot
		where TRuleIn : class, IRuleElement
		where TRuleOut : class, IRuleElement
	{
		public ContextSensitiveRewriter(IIdentity prv)
			: base(prv)
		{
			this.rcfg = new RewriterConfig();
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		TfsUnifier u;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TfsUnifier TfsUnifier
		{
			get { return this.u; }
			set { this.u = value; }
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		RewriterConfig rcfg;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public RewriterConfig Config
		{
			get { return rcfg; }
			set { rcfg = value; }
		}

		public R[] RewriteRules;

		public int ifeat_output;

		public int c_rules_applied;

#if rewriter_debugging
		Restrictor rst;
#endif

		public void Rewrite(R[] rules, int ifeat_output)
		{
			this.RewriteRules = rules;
			this.ifeat_output = ifeat_output;

			if (u == null)
				throw new Exception("unifier not set");

#if rewriter_debugging
			if (this is TokenMappingLattice)
			{
				rst = new Restrictor(u.ftm, Tfs.Flags.None, new[] { "+LL", "+TG" });
			}
#endif

			foreach (var rule in RewriteRules)
			{
				if (Edges.Any(e => e.Source == null || e.Target == null))
					Nop.X();

				StartRule(rule);

				int c_app = 0;

				while (ApplyRule(rule))
				{
					if (c_app++ == 0)
					{
						if (++c_rules_applied > rcfg.RuleApplicationLimitTotal)
							throw new RewriterException("Total rule application limit ({0}) exceeded. Last rule applied was '{1}'", rcfg.RuleApplicationLimitTotal, rule.Name);
					}
					if (c_app > rcfg.RuleApplicationLimit)
						throw new RewriterException("Rule '{0}' exceeded application limit ({1})", rule.Name, rcfg.RuleApplicationLimit);
				}
			}
		}

		///////////////////////////////////////////////////////////////////////
		/// functions for derived rewriter classes to implement the engine behavior
		/// 
		protected abstract void SetOutputEdgePositions(Hypothesis hypothesis, LatticeEdge[] new_edges);

		//protected abstract void GetOutputPosition(Hypothesis h, int ix);

		protected virtual void StartRule(R rule) { }

		protected abstract IEnumerable<Hypothesis> BuildHypotheses(R rule);

		public abstract TLat CreateElement(ITfsSlot ts);
		///
		///////////////////////////////////////////////////////////////////////

		//returns the hypothesis that eventually was used in the match, or null if no matches were found
		protected bool ApplyRule(R rule)
		{
			foreach (var hypothesis in BuildHypotheses(rule))
			{
				Debug.Assert(!hypothesis.Contains(null));

				var arr = rule.ContextInput.Select(dx => new Pairing<ITfsSlot>(dx, hypothesis[dx.Index].data));

				var unify_out = Unification.UnifySectionsIntoShell(rule.Expanded, arr, Tfs.Flags.RootCoverage);

#if rewriter_debugging
				var pairing = rule.ContextInput.Select(r_els =>
				{
					var hyp_el = hypothesis[r_els.Index];
					return new
					{
						rule_el = r_els as tmr.SrcElem,
						hyp_el,
						hyp_tok = hyp_el.data as MappingToken,
					};
				});
				if (this is TokenMappingLattice)
				{
					var tmrule = rule as TokenMappingRule;
					if (tmrule == null || !tmrule.HasRegex)
					{
						var prx = pairing.First();

						TokenMapper.tw.WriteLine("[cm] {0} {1}, arg {2} with chart item {3} ({4})",
									unify_out == null ? "checked" : "MATCHED",
									rule.Name,
									prx.rule_el.ModeIx.ToString(),
									prx.hyp_el._id.ToString(),
									"`" + prx.hyp_tok.SurfaceForm + "'"
									);
					}
				}
#endif
				if (unify_out != null)
				{
					unify_out._set_trace(this);

#if rewriter_debugging
					if (this is TokenMappingLattice)
					{

						sbb = new StringBuilder();

						s_outputs = String.Format("[cm] {0} fired: {1}", rule.Name,
											pairing.Select(pr => pr.rule_el.ModeIx.ToString() + ":" + pr.hyp_el._id.ToString() + " ").StringJoin(""));
						//pairing.Select(pr => pr.hyp_tok.ToYY(pr.hyp_el._id, 1)).StringJoin(", ")
					}
#endif

					var bb = ModifyAccordingToHypothesis(unify_out, hypothesis);

#if rewriter_debugging
					if (this is TokenMappingLattice)
					{
						TokenMapper.tw.WriteLine(s_outputs);
						TokenMapper.tw.Write(sbb.ToString());
					}
#endif

					if (bb)
						return true;
				}
			}
			return false;
		}

#if rewriter_debugging
		String s_outputs;
		StringBuilder sbb;
#endif

		protected bool ModifyAccordingToHypothesis(ArrayTfs unify_result, Hypothesis h)
		{
			var rule = h.rule;

			if (h.Count != rule.ContextInputCount)
				throw new Exception();

			var inps = rule.Input.Select(x => h[x.Index]).ToArray();

			int c = rule.Output.Length;

			int cancelled = 0;
			if (c > 0)
				cancelled = deploy_outputs(inps, h, unify_result);

			foreach (var inp in inps)
				if (inp != null)
					inp.Remove();

			if (base.EdgeCount > rcfg.EdgeLimit)
				throw new RewriterException("Edge limit ({0}) exceeded.", rcfg.EdgeLimit);

			return cancelled < c;
		}

		int deploy_outputs(LatticeEdge[] inps, Hypothesis h, ArrayTfs unify_result)
		{
			var rule = h.rule;

			int c = rule.Output.Length;

			TfsSlot[] output_slots;

			if ((output_slots = unify_result[ifeat_output].ListSlots).Length != c)
				throw new RewriterException("Wrong number of slots in rule-like tfs");
#if DEBUG
			if (!output_slots.IsDistinct(TfsSlot.SubstructureComparer))
				throw new Exception("tmr rule produced identical outputs");
#endif

			h.ApplyModsToUnifyResult(unify_result);	/// (i.e. burn RegEx results into the TFS, if any)

			TfsSlot ts_new;

			var output_edges = new LatticeEdge[c];

			for (int i = 0; i < c; i++)
			{
				ts_new = output_slots[i];

				TLat data = CreateElement(ts_new);

				var ne = output_edges[i] = AddEdge(data);

#if rewriter_debugging
				if (this is TokenMappingLattice)
				{
					s_outputs += String.Format("O{0}:{1} ", i + 1, ne._id);

					sbb.AppendFormat("I [{0} () -1--1 <{1}:{2}> \"\" \"{3}\" {{}} {{{4}}}] < blk: 0 >\r\n",
						ne._id,
						(data as MappingToken).SourceCharacterStart,
						(data as MappingToken).SourceCharacterEnd,
						(data as MappingToken).SurfaceForm,
						(data as MappingToken).PosProbs.OrderBy(z => z.pos).Select(z => String.Format("{0} {1:G2}", z.pos, z.p)).StringJoin(" ")
						);

					var ss = Tfs.MonospaceFormatter.Format(data, false, true, rst)
							.Replace(@"
                 [ +TAG string,
                   +PRB string ]", "")
							.Replace(@"
         [ +INITIAL luk,
           +CASE    token_case ]", "")
							.Replace(@"
         [ +INITIAL luk ]", "")
							.Replace(@"*diff-list*
               [ LIST *list*,
                 LAST *list* ]", "*diff-list*")
							.Replace(@"token_head
               [ +TI string ]", "token_head")
							.Replace(@"token_trait
         [ +UW bool,
           +IT italics,
           +LB *diff-list*,
           +RB *diff-list*,
           +LD *diff-list*,
           +RD *diff-list*,
           +HD token_head ]", "token_trait");

					sbb.AppendLine(ss);
				}
#endif
			}

			SetOutputEdgePositions(h, output_edges);

			int cancelled = 0;
			for (int i = c - 1; i >= 0; i--)
			{
				var ne = output_edges[i];

				foreach (var cospanning in CospanningEdges(ne))
				{
					if (cospanning.data.SubstructureEquals(ne.data))
					{
						var iix = Array.IndexOf(inps, cospanning);
						if (iix != -1)
							inps[iix] = null;

#if rewriter_debugging
						//sbb.AppendFormat("DELETING {0}\r\n", ne._id);
#endif

						ne.Remove();
						cancelled++;
						break;
					}
				}
			}

			return cancelled;
		}

		[DebuggerDisplay("{ToString(),nq}")]
		public class Hypothesis : IReadOnlyList<LatticeEdge>, IEquatable<Hypothesis>
		{
			public Hypothesis(ContextSensitiveRewriter<R, TLat, TRuleIn, TRuleOut> csr, R rule, int num_els)
			{
				this.csr = csr;
				this.rule = rule;
				this.edges = new LatticeEdge[num_els];
			}
			public Hypothesis(ContextSensitiveRewriter<R, TLat, TRuleIn, TRuleOut> csr, R rule, IReadOnlyList<LatticeEdge> edges)
				: this(csr, rule, edges.Count)
			{
				for (int i = 0; i < this.edges.Length; i++)
					this.edges[i] = edges[i];
			}
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			readonly ContextSensitiveRewriter<R, TLat, TRuleIn, TRuleOut> csr;
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public readonly R rule;
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			readonly LatticeEdge[] edges;

#if rewriter_debugging
			public System.Text.RegularExpressions.Match[] __matches;
#endif

			public virtual void ApplyModsToUnifyResult(Tfs unify_result)
			{
			}

			public bool Contains(LatticeEdge e) { return Array.IndexOf<LatticeEdge>(edges, e) != -1; }

			public LatticeEdge this[int index] { [DebuggerStepThrough] get { return edges[index]; } }

			public int Count { get { return edges.Length; } }

			public IEnumerator<LatticeEdge> GetEnumerator() { return edges.Enumerator(); }

			System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }

			public override String ToString()
			{
				return String.Format("{0} {1}",
					rule.Name,
					edges
						.Select(e => e == null ? "(null)" : e.data == null ? "(null-data)" : "[" + e.data.ToString() + "]")
						.StringJoin(" "));
			}

			public override int GetHashCode()
			{
				return rule == null || csr == null || edges == null ?
					0 : 1;
				//rule.GetHashCode() ^ csr.GetHashCode() ^ edges.HashAll();
			}
			public override bool Equals(Object obj)
			{
				return obj is Hypothesis && Equals((Hypothesis)obj);
			}
			public bool Equals(Hypothesis other)
			{
				if ((Object)other == default(Object))
					return false;
				if ((Object)other == (Object)this)
					return true;
				if (other.rule != this.rule || other.csr != this.csr || other.Count != edges.Length)
					return false;
				LatticeEdge[] rge;
				if ((rge = other.edges) != edges)
					for (int i = 0; i < edges.Length; i++)
						if (rge[i] != edges[i])
							return false;
				return true;
			}
			public static bool operator ==(Hypothesis h1, Hypothesis h2)
			{
				return (Object)h1 == (Object)h2 || ((Object)h1 != default(Object) && h1.Equals(h2));
			}
			public static bool operator !=(Hypothesis h1, Hypothesis h2)
			{
				return (Object)h1 != (Object)h2 && ((Object)h1 == default(Object) || !h1.Equals(h2));
			}
		};
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class pos_con_base<R, TLat, TRuleIn, TRuleOut> : ContextSensitiveRewriter<R, TLat, TRuleIn, TRuleOut>
		where R : PosConRewriteRule<TRuleIn, TRuleOut>
		where TLat : class, ITfsSlot
		where TRuleIn : class, IRuleElement
		where TRuleOut : class, IRuleElement
	{
		public pos_con_base(IIdentity prv)
			: base(prv)
		{
		}

		protected override IEnumerable<Hypothesis> BuildHypotheses(R rule)
		{
			if (rule.ContextInputCount == 1)
				return Edges.Select(e => new Hypothesis(this, rule, new LatticeEdge[] { e }));
			else
				return FindPosConSolutions(rule, 0);
		}

		IEnumerable<Hypothesis> FindPosConSolutions(R rule, int index)
		{
			if (index >= rule.PositionConstraints.Length)
			{
				yield return new Hypothesis(this, rule, rule.ContextInputCount);
			}
			else
			{
				var hypotheses = FindPosConSolutions(rule, index + 1);
				var poscon = rule.PositionConstraints[index];

				//pass through output constraints without doing anything
				if (poscon.mi1.mode == Element.Mode.OUTPUT || poscon.mi2.mode == Element.Mode.OUTPUT)
				{
					foreach (var hyp in hypotheses)
						yield return hyp;
				}
				else
				{
					foreach (var hyp in hypotheses)
						foreach (var new_hyp in FindSinglePosConSolutions(poscon, hyp))
							yield return new_hyp;
				}
			}
		}


		IEnumerable<Hypothesis> FindSinglePosConSolutions(PositionalConstraint pc, Hypothesis h)
		{
			var rule = h.rule;
			int rix_l = rule[pc.mi1].Index;
			int rix_r = rule[pc.mi2].Index;

			if (pc.mi1.mode == Element.Mode.OUTPUT || pc.mi2.mode == Element.Mode.OUTPUT)
				yield return h;

			else if (h[rix_l] != null && h[rix_r] != null)
			{
				if (CheckPosConOnTokens(h[rix_l], h[rix_r], pc.op))
					yield return h;
			}
			else if (h[rix_l] != null)
			{
				foreach (var edge in positional_candidates(h[rix_l], pc.op, false).Where(x => !h.Contains(x)))
				{
					var new_hyp = h.ToArray();
					new_hyp[rix_r] = edge;
					yield return new Hypothesis(this, rule, new_hyp);
				}
			}
			else if (h[rix_r] != null)
			{
				foreach (var edge in positional_candidates(h[rix_r], pc.op, true).Where(x => !h.Contains(x)))
				{
					var new_hyp = h.ToArray();
					new_hyp[rix_l] = edge;
					yield return new Hypothesis(this, rule, new_hyp);
				}
			}
			else
			{
				foreach (var edge1 in Edges.Where(x => !h.Contains(x)))
				{
					foreach (var edge2 in positional_candidates(edge1, pc.op, false).Where(x => !h.Contains(x)))
					{
						var new_hyp = h.ToArray();
						new_hyp[rix_l] = edge1;
						new_hyp[rix_r] = edge2;

						if (edge1 != edge2)
							yield return new Hypothesis(this, rule, new_hyp);
					}
				}
			}
		}

		/// <summary>
		/// this finds edges that are in a specific relationship to the parameter edge (tok)
		/// </summary>
		IEnumerable<LatticeEdge> positional_candidates(LatticeEdge edge, PositionalConstraint.Op op, bool reversed)
		{
			switch (op)
			{
				case PositionalConstraint.Op.LEFT_ADJ:
					return !reversed ? edge.Target.rights : positional_candidates(edge, PositionalConstraint.Op.RIGHT_ADJ, false);

				case PositionalConstraint.Op.RIGHT_ADJ:
					return !reversed ? edge.Source.lefts : positional_candidates(edge, PositionalConstraint.Op.LEFT_ADJ, false);

				case PositionalConstraint.Op.PRECEEDS:
					return !reversed ? edge.Target.DescendantEdges() : positional_candidates(edge, PositionalConstraint.Op.SUCCEEDS, false);

				case PositionalConstraint.Op.SUCCEEDS:
					return !reversed ? edge.Source.AnscestorEdges() : positional_candidates(edge, PositionalConstraint.Op.PRECEEDS, false);

				case PositionalConstraint.Op.COVERS:
					return !reversed ? Edges.Where(x => CheckPosConOnTokens(edge, x, PositionalConstraint.Op.COVERS)) :
									   Edges.Where(x => CheckPosConOnTokens(x, edge, PositionalConstraint.Op.COVERS));

				default:
					return NoEdges;
			}
		}

		/// <summary>
		/// this checks whether the two parameter edges (tok1 and tok2) are in the specified relationship
		/// </summary>
		public bool CheckPosConOnTokens(LatticeEdge tok1, LatticeEdge tok2, PositionalConstraint.Op op)
		{
			switch (op)
			{
				case PositionalConstraint.Op.LEFT_ADJ:
					return tok1.Target == tok2.Source;

				case PositionalConstraint.Op.RIGHT_ADJ:
					return tok2.Target == tok1.Source;

				case PositionalConstraint.Op.PRECEEDS:
					return IsAncestor(tok1.Target, tok2.Source);

				case PositionalConstraint.Op.SUCCEEDS:
					return IsAncestor(tok2.Target, tok1.Source);

				case PositionalConstraint.Op.COVERS:
					return (tok1.Source == tok2.Source || IsAncestor(tok1.Source, tok2.Source))
						&& (tok1.Target == tok2.Target || IsAncestor(tok2.Target, tok1.Target));

				default:
					return false;
			}
		}

		protected sealed override void SetOutputEdgePositions(Hypothesis h, LatticeEdge[] new_edges)
		{
			var rule = h.rule;
			var out_poscons = rule.PositionConstraints.Where(pc => pc.mi1.mode == Element.Mode.OUTPUT || pc.mi2.mode == Element.Mode.OUTPUT);

			foreach (var pc in out_poscons)
			{
				if (pc.op == PositionalConstraint.Op.LEFT_ADJ)
				{
					if (pc.mi1.mode == Element.Mode.OUTPUT && pc.mi2.mode == Element.Mode.OUTPUT)
					{
						throw new Exception("won't end well");
						LatticeVertex v = NewVertex();
						new_edges[pc.mi1.index].Target = v;
						new_edges[pc.mi2.index].Source = v;
					}
					else if (pc.mi1.mode == Element.Mode.OUTPUT)
					{
						new_edges[pc.mi1.index].Target = h[rule[pc.mi2].Index].Source;
					}
					else if (pc.mi2.mode == Element.Mode.OUTPUT)
					{
						new_edges[pc.mi2.index].Source = h[rule[pc.mi2].Index].Target;
					}
					else
					{
						throw new Exception();
					}
				}
				else if (pc.op == PositionalConstraint.Op.COVERS)
				{
					Debug.Assert(pc.mi1.mode == Element.Mode.OUTPUT && pc.mi2.mode != Element.Mode.OUTPUT);

					set_source_target(ref new_edges[pc.mi1.index], h[rule[pc.mi2].Index]);
				}
				else
				{
					throw new Exception();
				}
			}

			if (new_edges.Any(x => x.Source == null || x.Target == null))
				throw new Exception();
		}

		void set_source_target(ref LatticeEdge e, LatticeEdge ee)
		{
			set_source(ref e, ee.Source);
			set_target(ref e, ee.Target);
		}
		void set_source(ref LatticeEdge e, LatticeVertex v)
		{
			if (e.Source != null)
				Nop.X();
			if (e.Source == null || IsAncestor(v, e.Source))
				e.Source = v;
		}
		void set_target(ref LatticeEdge e, LatticeVertex v)
		{
			if (e.Target != null)
				Nop.X();
			if (e.Target == null || IsAncestor(e.Target, v))
				e.Target = v;
		}

		//protected override void GetOutputPosition(Hypothesis h, int ix, out LatticeVertex v_src, out LatticeVertex v_tgt)
		//{
		//	foreach (var pc in h.rule.PositionConstraints)
		//	{


		//	}
		//}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class PosConRewriteRule<TRuleIn, TRuleOut> : RewriteRule<TRuleIn, TRuleOut>
		where TRuleIn : class, IRuleElement
		where TRuleOut : class, IRuleElement
	{
		public PosConRewriteRule(Grammar g, Type t, ConsDefs consdef)
			: base(g, t, consdef)
		{
			PositionConstraints = alib.Collections.Collection<PositionalConstraint>.None;
		}

		[DebuggerDisplay("{System.String.Join(\" \",PositionConstraints),nq}")]
		public PositionalConstraint[] PositionConstraints;

		protected override void AnalyzeRuleBehavior(Tfs tfs)
		{
			var ps = tfs[em.TokenMappingGeometry.ChartMappingPositionPath].StringValue;
			if (ps != null)
			{
				PositionConstraints = ps.Split(Charset.comma_ws, StringSplitOptions.RemoveEmptyEntries)
										.SelectMany(s => PositionalConstraint.ExtractDuples(s))
										.ToArray();

				//Debug.Print("========\r\n{0}\r\n{1}", ps, PositionConstraints.Select(pc => "[" + pc.ToString() + "]").StringJoin(" "));
				//Debug.Print("{0,-15} {1}", this.Name, PositionConstraints.Select(pc => "[" + pc.ToString() + "]").StringJoin(" "));
			}
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	/// context-sensitive lattice rewriter for rules with positional constraints
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class PosConRewriter<R, TLat, TRuleIn, TRuleOut> : pos_con_base<R, TLat, TRuleIn, TRuleOut>
		where R : PosConRewriteRule<TRuleIn, TRuleOut>
		where TLat : class, ITfsSlot
		where TRuleIn : class, IRuleElement
		where TRuleOut : class, IRuleElement
	{
		public PosConRewriter(IIdentity prv)
			: base(prv)
		{
#if old_way
			this.current_matches = new HashSet<Hypothesis>();
#endif
		}

#if old_way
		HashSet<Hypothesis> current_matches;

		protected sealed override void StartRule(R rule)
		{
#if rewriter_debugging
			Debug.Print("[cm]-- start rule {0}", rule.Name);
#endif
			current_matches.Clear();
		}

		protected override IEnumerable<Hypothesis> BuildHypotheses(R rule)
		{
			foreach (var h in base.BuildHypotheses(rule))
				if (current_matches.Add(h))
					yield return h;
		}
#endif
	};

	namespace regex
	{
		using SysRegex = System.Text.RegularExpressions.Regex;
		using SysMatch = System.Text.RegularExpressions.Match;
		using SysRegexOptions = System.Text.RegularExpressions.RegexOptions;
		using Element = tmr.Element;

		[DebuggerDisplay("{ToString(),nq}")]
		public class Regex : SysRegex
		{
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public static Regex[] None = Collection<Regex>.None;

			public Regex(FsPath fsp, String pattern)
				: base(pattern, SysRegexOptions.Compiled)
			{
				this.mode_ix = fsp.im.em.TokenMappingGeometry.CSRModeIx(fsp);
				this.fsp = fsp.SubPath(mode_ix.index + 2);
			}
			public FsPath fsp;
			public int match_index;
			public ModeIx mode_ix;

			public String Pattern { get { return base.pattern; } }

			public override String ToString()
			{
				return String.Format("{0}-{1} ...{2,-18} {3}", mode_ix, match_index, fsp.ToString(), base.pattern);
			}
		};

		[DebuggerDisplay("{ToString(),nq}")]
		public struct RegexReplaceRaw
		{
			public RegexReplaceRaw(FsPath fsp, String s_repl)
			{
				this.mode_ix = fsp.im.em.TokenMappingGeometry.CSRModeIx(fsp);
				this.fsp = fsp.SubPath(mode_ix.index + 2);
				this.s_repl = s_repl;
			}
			public ModeIx mode_ix;
			public FsPath fsp;
			public String s_repl;

			public override String ToString()
			{
				return String.Format("{0} ...{1,-18} {2}", mode_ix, fsp.ToString(), s_repl);
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public abstract class RegexRewriter<R, TLat, TRuleIn, TRuleOut> : PosConRewriter<R, TLat, TRuleIn, TRuleOut>
			where R : RegexRewriterRule<TRuleIn, TRuleOut>
			where TLat : class, ITfsSlot
			where TRuleIn : class, IRuleElement
			where TRuleOut : class, IRuleElement
		{
			public RegexRewriter(IIdentity prv)
				: base(prv)
			{
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public abstract class RegexRewriterRule<TRuleIn, TRuleOut> : PosConRewriteRule<TRuleIn, TRuleOut>
			where TRuleIn : class, IRuleElement
			where TRuleOut : class, IRuleElement
		{
			public RegexRewriterRule(Grammar g, Type t, ConsDefs consdef)
				: base(g, t, consdef)
			{
			}

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public List<Regex> match_raw;
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public List<RegexReplaceRaw> repl_raw;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public bool HasRegex { get { return RegexCount > 0; } }

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public int RegexCount;


			protected override void discarding_tdl(TfsBuilder.Tdl tfs_tdl)
			{
				this.match_raw = tfs_tdl.rx_match;
				this.repl_raw = tfs_tdl.rx_repl;
			}

			public override String ToString()
			{
				return String.Format("{0,-34}  {1,-12} {2} {3}",
					base.Name,
					ContextInputOutput.Select(csre => csre.ModeIx.ToString()).StringJoin(" "),
					HasRegex ? "R" + RegexCount.ToString() : "  ",
					PositionConstraints.StringJoin(" "));
			}
		};
	};
}
